/**
 * 计算RGB相似度
 * 本段代码来源： https://blog.csdn.net/luoluoyang23/article/details/122784426
 */

//用于LAB模型计算
const param_1_3 = 1.0 / 3.0;
const param_16_116 = 16.0 / 116.0;
const Xn = 0.950456;
const Yn = 1.0;
const Zn = 1.088754;

//色彩矫正
// function gamma(colorX) {
//   //判断是否不是浮点数
//   if (~~colorX === colorX) {
//     colorX = parseFloat(colorX + '');
//   }
//   return colorX > 0.04045
//     ? Math.pow((colorX + 0.055) / 1.055, 2.4)
//     : colorX / 12.92;
// }

//RGB转换成XYZ
function RGB2XYZ(rgbA: number[]) {
  const [colorR, colorG, colorB] = rgbA;
  const colorX = 0.4124564 * colorR + 0.3575761 * colorG + 0.1804375 * colorB;
  const colorY = 0.2126729 * colorR + 0.7151522 * colorG + 0.072175 * colorB;
  const colorZ = 0.0193339 * colorR + 0.119192 * colorG + 0.9503041 * colorB;
  return [colorX, colorY, colorZ];
}

//XYZ转换成LAB
function XYZ2LAB(xyz: number[]) {
  let [colorX, colorY, colorZ] = xyz;
  colorX = colorX / Xn;
  colorY = colorY / Yn;
  colorZ = colorZ / Zn;

  const fX =
    colorX > 0.008856
      ? Math.pow(colorX, param_1_3)
      : 7.787 * colorX + param_16_116;
  const fY =
    colorY > 0.008856
      ? Math.pow(colorY, param_1_3)
      : 7.787 * colorY + param_16_116;
  const fZ =
    colorZ > 0.008856
      ? Math.pow(colorZ, param_1_3)
      : 7.787 * colorZ + param_16_116;
  let colorL = parseFloat('116') * fY - parseFloat('16');
  colorL = colorL > parseFloat('0.0') ? colorL : parseFloat('0.0');
  const colorA = parseFloat('500') * (fX - fY);
  const colorB = parseFloat('200') * (fY - fZ);
  return [colorL, colorA, colorB];
}

//彩度计算
function computeCaidu(colorA: number, colorB: number) {
  return Math.pow(colorA * colorA + colorB * colorB, 0.5);
}

//色调角计算
function computeSeDiaoJiao(colorA: number, colorB: number) {
  if (colorA === 0) {
    return 90;
  }

  const h = (180 / Math.PI) * Math.atan(colorB / colorA);
  let hab;
  if (colorA > 0 && colorB > 0) {
    hab = h;
  } else if (colorA < 0 && colorB > 0) {
    hab = 180 + h;
  } else if (colorA < 0 && colorB < 0) {
    hab = 180 + h;
  } else {
    hab = 360 + h;
  }
  return hab;
}

//比较色值近似度，使用CIEDE2000色差公式
function differenceColor(firstColor: number[], secondColor: number[]) {
  const L1 = firstColor[0];
  const A1 = firstColor[1];
  const B1 = firstColor[2];
  const L2 = secondColor[0];
  const A2 = secondColor[1];
  const B2 = secondColor[2];

  //《现代颜色技术原理及应用》p88参考常量

  const kL = parseFloat('1');
  const kC = parseFloat('1');
  const kH = parseFloat('1');
  const mean_Cab = (computeCaidu(A1, B1) + computeCaidu(A2, B2)) / 2;
  const mean_Cab_pow7 = Math.pow(mean_Cab, 7);
  //权重，色值规律变化时人眼观察并不是规律的，因为人眼对不同通道颜色感知不同，增加权重缓解这个问题
  const G =
    0.5 *
    (1 - Math.pow(mean_Cab_pow7 / (mean_Cab_pow7 + Math.pow(25, 7)), 0.5));
  const LL1 = L1;
  const aa1 = A1 * (1 + G);
  const bb1 = B1;
  const LL2 = L2;
  const aa2 = A2 * (1 + G);
  const bb2 = B2;
  const CC1 = computeCaidu(aa1, bb1);
  const CC2 = computeCaidu(aa2, bb2);
  const hh1 = computeSeDiaoJiao(aa1, bb1);
  const hh2 = computeSeDiaoJiao(aa2, bb2);
  const delta_LL = LL1 - LL2;
  const delta_CC = CC1 - CC2;
  const delta_hh = computeSeDiaoJiao(aa1, bb1) - computeSeDiaoJiao(aa2, bb2);
  const delta_HH =
    2 * Math.sin((Math.PI * delta_hh) / 360) * Math.pow(CC1 * CC2, 0.5);

  //计算加权函数
  const mean_LL = (LL1 + LL2) / 2;
  const mean_CC = (CC1 + CC2) / 2;
  const mean_hh = (hh1 + hh2) / 2;
  const SL =
    1 +
    (0.015 * Math.pow(mean_LL - 50, 2)) /
      Math.pow(20 + Math.pow(mean_LL - 50, 2), 0.5);
  const SC = 1 + 0.045 * mean_CC;
  const T =
    1 -
    0.17 * Math.cos(((mean_hh - 30) * Math.PI) / 180) +
    0.24 * Math.cos((2 * mean_hh * Math.PI) / 180) +
    0.32 * Math.cos(((3 * mean_hh + 6) * Math.PI) / 180) -
    0.2 * Math.cos(((4 * mean_hh - 63) * Math.PI) / 180);
  const SH = 1 + 0.015 * mean_CC * T;

  //计算RT
  const mean_CC_pow7 = Math.pow(mean_CC, 7);
  const RC = 2 * Math.pow(mean_CC_pow7 / (mean_CC_pow7 + Math.pow(25, 7)), 0.5);
  const delta_xita = 30 * Math.exp(-Math.pow((mean_hh - 275) / 25, 2));
  const RT = -Math.sin((2 * delta_xita * Math.PI) / 180) * RC;

  const L_item = delta_LL / (kL * SL);
  const C_item = delta_CC / (kC * SC);
  const H_item = delta_HH / (kH * SH);

  //参考常量E00
  return Math.pow(
    L_item * L_item + C_item * C_item + H_item * H_item + RT * C_item * H_item,
    0.5
  );
}

//计算两个RGB颜色近似程度，仅调用该文件中的方法，只是提供调用流程参考
export function differenceRGB(rgbA: number[], rgbB: number[]) {
  const xyzA = RGB2XYZ(rgbA);
  const xyzB = RGB2XYZ(rgbB);
  const labA = XYZ2LAB(xyzA);
  const labB = XYZ2LAB(xyzB);
  return differenceColor(labA, labB);
}
